#####################################################################
# Product:      KnowledgeEngine (Plone Edition)
#
# File:         KERFProcessor.py
#
# Copyright:    Copyright (c) 2004, Faulkner Technologies
#
# Author:       Brian Oliver
#
# Description:  This file defines functions for evaluating/replacing
#               Knowledge Engine Report Field (KERF) expressions with 
#               information in an assessment.
#
#               A KERF is a fragment of xml that specifies a python
#               expression, possibly made up of references to 
#               component and properties which when resolved and
#               evaluated produces a string.
#
#               Example 1: Simple KERF
#               ------------------------
#               <kerf>hello world</kerf>
#
#                   When evaluated returns the string 'hello world'
#
#               Example 2: KERF in other text
#               ------------------------
#               <b><kerf>hello world</kerf></b>
#
#                   When evaluated returns the string '<b>hello world</b>'
#
#               Example 3: KERF with an equation
#               ------------------------
#               <b><kerf>3 * 4</kerf></b>
#
#                   When evaluated returns the string '<b>12</b>'
#
#               Example 4: KERF with a component property reference
#               ------------------------
#               <i><kerf><$ txt1234:title $></kerf></i>
#
#                   When evaluated returns the string '<i>X</i>', 
#                   where X is the title property of the component
#                   with the identity 'txt1234'
#
#               Example 5: KERF with a property reference (componentless)
#               ------------------------
#               <i><kerf><$ value $></kerf></i>
#
#                   When evaluated returns the string '<i>X</i>', 
#                   where X is the value of property of the component
#                   passed into the resolveKERF function (the local component)
#
#               Example 6: KERF with a property reference in an equation
#               ------------------------
#               <i><kerf><$ value $> * 5.5</kerf></i>
#
#                   When evaluated returns the string '<i>X</i>', 
#                   where X is the value of property of the component
#                   passed into the resolveKERF function (the local component)
#                   multiplied by 5.3
#
# See Also:     Report
#####################################################################

#####################################################################
## KnowledgeEngine Library Imports
from Common import *

#####################################################################
## Zope Library Imports
# (none)
 
#####################################################################
## Python Library Imports
# (none)

#####################################################################
            
def resolveKERFProperties(kerf, assessment, component=None):
    """
        PRIVATE: Resolves all of the <$ ... $> component property references
        in the specified kerf string using the provided assessment
        component provide references to 
        local objects for componentless property references

        RETURNS: a python expression that may be evaluated with eval
    """
    
    context = assessment.getModel().portal_url.getPortalObject()
    
    #component property tokens
    START_TOKEN = "<$"
    END_TOKEN = "$>"

    #the result thus far
    result = kerf

    #find the first property markup
    startIndex = result.find(START_TOKEN)

    #keep resolving properties until the kerf is resolved
    while startIndex >= 0:
        #get the endIndex
        endIndex = result.find(END_TOKEN, startIndex)

        skipPart = False

        #was an endIndex located
        if endIndex >= 0:
            #get the property to resolve
            property = result[startIndex + len(START_TOKEN):endIndex]

            numSections = property.count( ":" )

            #find the location of the identity:property separator
            #propertyIndex = property.find(":")

            if numSections == 0:    # No component id specified, use the current component                
                identity = None
                if hasattr( component, 'getIdentity' ):
                    identity = component.getIdentity()
                    
                name = property.strip()
                
            elif numSections == 1:   # single component ID specified, use the specified component
                
                identity, name = property.split( ":" )
                identity, name = identity.strip(), name.strip()
                
                component = context.getComponentByIdentity( identity )
                
            elif numSections >= 2:  # Two component ids specified, treat as a join
                                                
                parts = property.split(":")
                parts.reverse()
                                                
                fromComponentIdentity = None
                toComponentIdentity   = None                                                                                
                                                
                print "KERF: " + property
                                                
                name = parts[0].strip()
                                                
                while len(parts) > 1:
                                                            
                    if toComponentIdentity: fromComponentIdentity = toComponentIdentity
                    else:                   fromComponentIdentity = parts.pop()
                                        
                    toComponentIdentity   = parts.pop()
                    
                    fromComponentIdentity, toComponentIdentity = fromComponentIdentity.strip(), toComponentIdentity.strip()
                    
                    
                    identity = toComponentIdentity
                    
                    print "Link: %s -> %s" % ( fromComponentIdentity, toComponentIdentity )
                    
                    #fromComponentIdentity = parts.pop() 
                    
                                                                                                                                                                                
                    #fromComponentIdentity, identity, name = property.split(":")
                    #fromComponentIdentity, identity, name = fromComponentIdentity.strip(), identity.strip(), name.strip()
                
                    #fromComponent = context.getComponentByIdentity( fromComponentIdentity )
                    component = context.getComponentByIdentity( toComponentIdentity )                                         
                    
                    linkToAssessmentIdentity = assessment.getComponentByIdentityValue( fromComponentIdentity )
                    
                    print "assessment[ %s ] = %s" % ( fromComponentIdentity, linkToAssessmentIdentity )
                    
                    if type( linkToAssessmentIdentity ) in ( list, tuple ):
                        linkToAssessmentIdentity = linkToAssessmentIdentity[0]
                                                                  
                    if linkToAssessmentIdentity:
                        assessment = context.knowledgeengine_assessmentstorageservice.loadAssessment( linkToAssessmentIdentity )                        
                    else:
                        break
                    
                    print "Finally looking for %s in %s" % ( component.getIdentity(), assessment.getIdentity() )
                                            

            #is the component defined?
            if hasattr(component,'getComponentProperty'):

                #fetch the value of the property from the component using the provided assessment
                #value = component.getComponentProperty(identity, name, assessment)

                #print "%s.getComponentProperty( %s, %s, %s )" % ( component.getIdentity(), identity, name, assessment.getIdentity() )
                #print " == %s" % `value`
                
                #value = assessment.getComponentByIdentityValue( component.getIdentity() )
                value = component.getComponentProperty( component.getIdentity(), name, assessment )

                #was the property found?
                if value == None:
                    value = "<font color='red'>Unknown Property <b>" + name + "</b> for Component <b>" + identity + "</b></font>"    
                else:
                    value = str(value)
                    
                
                    

            # Allow dictionaries to be passed to KERF
            elif hasattr(component, '__getitem__'):
                try:
                    value = component[name]
                except KeyError:
                    value = "(Unknown value %s)" % name
            
            else:
                #unknown component
                value = "<font color='red'>Unknown Component <b>" + str(identity) + "</b> in Model</font>"

            #replace resolved property into kerf
            
            #if type(value) is str:
                #rvalue = value
            #else:                
                #rvalue = repr(value)
                
            # FIXMEFIXME - jamesd 2006-05-09 Fixing issue #570 - Is this correct? 
            # (Need to test this more)                 
            result = result[:startIndex] + `value` + result[endIndex+len(END_TOKEN):]

            #find the next property markup
            startIndex = result.find(START_TOKEN)

        else:
            startIndex = -1

    

    #return the result
    return result

#---------------------------------------------------------------#

from Products.KnowledgeEngine.Utils.helpers import formatThousands

kerf_namespace = {
    "formatThousands": formatThousands
}

def resolveKERF(content, assessment, component=None):
    """
        PUBLIC: Resolves the <kerf>...</kerf> (Knowledge Engine Report Fields)
        markups in the specified content (a string) given an
        assessment.
    """

    #start and end tokens
    START_TOKEN = "<kerf>"
    END_TOKEN = "</kerf>"

    #the result thus far
    result = content

    #find the first <kerf> markup
    startIndex = result.find(START_TOKEN)

    #keep resolving kerfs until the content is resolved
    while startIndex >= 0:
        #get the endIndex
        endIndex = result.find(END_TOKEN, startIndex)

        #was an endIndex located
        if endIndex >= 0:
            #get the kerf to resolve
            kerf = result[startIndex + len(START_TOKEN):endIndex]

            #resolve kerf properties
            kerf = resolveKERFProperties(kerf, assessment, component)

            #try to evaluate the kerf expression (which is a python expression)
            try:
                value = eval(kerf, kerf_namespace)
            except Exception, e:
                raise
                value = "<font color='red'>Could not evaluate <b>%s</b> in component <b>%s</b>: %s</font>" % (kerf,component and component.getIdentity(), str(e))

            #replace resolved kerf into content
            result = "%s%s%s" % (result[:startIndex], value, result[endIndex+len(END_TOKEN):])

            #find the next <kerf> markup
            startIndex = result.find(START_TOKEN)

        else:
            startIndex = -1

    #return the result
    return result

#---------------------------------------------------------------#

def updateKERFIdentities(kerf, identityMappings):
    """
        PRIVATE: Resolves the component identities for the specified 
        kerf <$ ... $>

        RETURNS: returns the kerf with old component identities
        replace with those as specified in the identityMappings
    """

    #component property tokens
    START_TOKEN = "<$"
    END_TOKEN = "$>"

    #the result thus far
    result = kerf

    #find the first property markup
    startIndex = result.find(START_TOKEN)

    #keep updating identities until the kerf is resolved
    while startIndex >= 0:
        #get the endIndex
        endIndex = result.find(END_TOKEN, startIndex)

        #was an endIndex located
        if endIndex >= 0:
            #get the property to resolve
            property = result[startIndex + len(START_TOKEN):endIndex]

            #find the location of the identity:property separator
            propertyIndex = property.find(":")

            #is a <$ component:property $> property provided?
            if propertyIndex >= 0:
                #yes... 

                #get component identity
                identity = property[:propertyIndex].strip()

                #is the identity in the identityMappings?                    
                if identityMappings.has_key(identity):
                    #get the new identity
                    identity = identityMappings[identity]

                #get component property name
                name = property[propertyIndex + 1:].strip()

                #replace resolved property into kerf
                result = result[:startIndex] + START_TOKEN + " " + identity + ":" + name + " " + END_TOKEN + result[endIndex+len(END_TOKEN):]

            else:                
                #no, so assume in the format <$ property $>

                #get component property name
                name = property.strip()

                #replace resolved property into kerf
                result = result[:startIndex] + START_TOKEN + " " + name + " " + END_TOKEN + result[endIndex+len(END_TOKEN):]

            #find the next property markup
            startIndex = result.find(START_TOKEN, startIndex + 1)

        else:
            startIndex = -1

    #return the result
    return result

#---------------------------------------------------------------#

def updateKERF(content, identityMappings):
    """
        PUBLIC: Updates the component identities in <kerf>...</kerf> 
        (Knowledge Engine Report Fields) markups in the specified 
        content (a string) given a mapping from old to new component identities.
    """

    #start and end tokens
    START_TOKEN = "<kerf>"
    END_TOKEN = "</kerf>"

    ###print "DEBUG: Report.updateKERF: Before [%s] with [%s]" % (content, repr(identityMappings))

    #the result thus far
    result = content

    #find the first <kerf> markup
    startIndex = result.find(START_TOKEN)

    #keep resolving kerfs until the content is resolved
    while startIndex >= 0:
        #get the endIndex
        endIndex = result.find(END_TOKEN, startIndex)

        #was an endIndex located
        if endIndex >= 0:
            #get the kerf to resolve
            kerf = result[startIndex + len(START_TOKEN):endIndex]

            ###print "DEBUG: Report.updateKERF: Before %s" % (kerf,)

            #resolve kerf properties
            kerf = updateKERFIdentities(kerf, identityMappings)

            ###print "DEBUG: Report.updateKERF: After %s" % (kerf,)

            #replace resolved kerf into content
            result = result[:startIndex] + START_TOKEN + kerf + END_TOKEN + result[endIndex+len(END_TOKEN):]

            #find the next <kerf> markup
            startIndex = result.find(START_TOKEN, startIndex + 1)

        else:
            startIndex = -1

    ###print "DEBUG: Report.updateKERF: After [%s] with [%s]" % (content, repr(identityMappings))

    #return the result
    return result

#---------------------------------------------------------------#

#####################################################################
